//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<CTexture2D10.cpp>
///	@path	~/src/graphics/dx10/
///	@date	2008/12/20
///	@desc	.

#include "config/config.h"

#include "lib/system/thread_debug.h"
#include "lib/math/math_win32.h"

#include "graphics/DxHelpers.h"
#include "graphics/dx10/CSprite10.h"
#include "graphics/dx10/CTexture2D10.h"

namespace xeres {

	IMPL_IOBJECT_CLASS( CTexture2D10 );

	// CTexture2D10
	CTexture2D10::CTexture2D10( ID3D10Texture2D * pTexture10 )
		: m_pTexture10( pTexture10 )
	{
		if( m_pTexture10 )
		{
			m_pTexture10->GetDesc( &m_desc );
		}
		else
		{
			memset( &m_desc , 0 , sizeof(D3D10_TEXTURE2D_DESC) );
		}
	}

	// ~CTexture2D10
	CTexture2D10::~CTexture2D10( void )
	{
		if( m_pTexture10 )
		{
			UINT rest = m_pTexture10->Release();
			if( rest != 0 )
			{
				TRACE_ERROR( _S("CTexture2D10::DTOR: Invalid IDirect3DTexture9 reference count.") );
				FATAL_ABORT( _S("Invalid ID3D10Texture2D reference count.") );
			}
		}
	}

	// OnLostDevice
	bool CTexture2D10::OnLostDevice( void )
	{
		// we don't need to re-create the texture.
		return true;
	}

	// OnResetDevice
	bool CTexture2D10::OnResetDevice( RefWeak<IDevice> device )
	{
		// we don't need to re-create the texture.
		return true;
	}

	// GetType
	TextureType CTexture2D10::GetType( void )
	{
		return TEXTURE_2D;
	}

	// GetFormat
	DXFormat CTexture2D10::GetFormat( void )
	{
		if( m_pTexture10 == NULL )
			return DXFormat();
		return m_desc.Format;
	}

	// GetSize
	Pair<int> CTexture2D10::GetSize( void )
	{
		if( m_pTexture10 == NULL )
			return Pair<int>( 0 , 0 );
		return Pair<int>( m_desc.Width , m_desc.Height );
	}

	// GetDepth
	size_t CTexture2D10::GetDepth( void )
	{
		FATAL_ABORT( _S("Try to get depth from 2D texture.") );
		return 0;
	}

	// IsWritable
	bool CTexture2D10::IsWritable( void )
	{
		return (m_desc.CPUAccessFlags & D3D10_CPU_ACCESS_WRITE) ? true : false;
	}
	// IsReadable
	bool CTexture2D10::IsReadable( void )
	{
		return (m_desc.CPUAccessFlags & D3D10_CPU_ACCESS_WRITE) ? true : false;
	}

	// Map
	bool CTexture2D10::Map( uint sub , ResourceMap& data )
	{
		if( m_pTexture10 == NULL )
			return false;

		if( sub >= m_desc.MipLevels )
		{
			TRACE_ERROR( _S("CTexture2D10::Map: Level exceeded limit.") );
			return false;
		}

		if( ( m_desc.CPUAccessFlags & D3D10_CPU_ACCESS_WRITE ) == 0 )
		{
			TRACE_ERROR( _S("CTexture2D10::Map: Texture cannot be accessed by CPU.") );
			return false;
		}

		D3D10_MAPPED_TEXTURE2D mapped;
		memset( &mapped , 0 , sizeof(D3D10_MAPPED_TEXTURE2D) );
		HRESULT hr = m_pTexture10->Map( sub , D3D10_MAP_WRITE_DISCARD , 0 , &mapped );
		if( FAILED(hr) )
		{
			TRACE_ERROR( _S("CTexture2D10::Map: Failed to lock texture: %s") ,
				d3d_get_err(hr) );
			return false;
		}
		size_t total = dxgi_get_color_size( m_desc.Format ) * m_desc.Height * mapped.RowPitch;
		data.Map( (byte*)mapped.pData , total , mapped.RowPitch , m_desc.Height , m_desc.Width );
		return true;
	}

	// ReadBack
	bool CTexture2D10::ReadBack( uint sub , ResourceMap& data , bool readonly )
	{
		if( m_pTexture10 == NULL )
			return false;

		if( sub >= m_desc.MipLevels )
		{
			TRACE_ERROR( _S("CTexture2D10::ReadBack: Level exceeded limit.") );
			return false;
		}

		if( ( m_desc.CPUAccessFlags & D3D10_CPU_ACCESS_READ ) == 0 )
		{
			TRACE_ERROR( _S("CTexture2D10::ReadBack: Texture cannot be accessed by CPU.") );
			return false;
		}
		if( !readonly && ( ( m_desc.CPUAccessFlags & D3D10_CPU_ACCESS_WRITE ) == 0 ) )
		{
			TRACE_ERROR( _S("CTexture2D10::ReadBack: Texture cannot be accessed by CPU.") );
			return false;
		}

		D3D10_MAPPED_TEXTURE2D mapped;
		memset( &mapped , 0 , sizeof(D3D10_MAPPED_TEXTURE2D) );
		HRESULT hr = m_pTexture10->Map( sub , readonly ? D3D10_MAP_READ : D3D10_MAP_READ_WRITE , 0 , &mapped );
		if( FAILED(hr) )
		{
			TRACE_ERROR( _S("CTexture2D10::ReadBack: Failed to lock texture: %s") ,
				d3d_get_err(hr) );
			return false;
		}
		size_t total = dxgi_get_color_size( m_desc.Format ) * m_desc.Height * mapped.RowPitch;
		data.Map( (byte*)mapped.pData , total , mapped.RowPitch , m_desc.Height , m_desc.Width );
		return true;
	}

	// Unmap
	bool CTexture2D10::Unmap( uint sub )
	{
		if( m_pTexture10 == NULL )
			return false;
		
		m_pTexture10->Unmap( sub );
		return true;
	}

	// Update
	bool CTexture2D10::Update( uint sub , const Box<uint>& size , const byte * data , uint row_pitch , uint depth_pitch )
	{
		ID3D10Device * device = NULL;
		m_pTexture10->GetDevice( &device );
		D3D10_BOX box;
		box.left = size.left();
		box.right = size.right();
		box.top = size.top();
		box.bottom = size.bottom();
		box.front = size.front();
		box.back = size.back();
		device->UpdateSubresource( m_pTexture10 , sub , &box , data , row_pitch , depth_pitch );
		return true;
	}

	// GetTextureInterface
	IUnknown * CTexture2D10::GetTextureInterface( void )
	{
		return m_pTexture10;
	}


	IMPL_IOBJECT_CLASS( CShaderTexture2D10 );

	// CShaderTexture2D10
	CShaderTexture2D10::CShaderTexture2D10( ID3D10Texture2D * tex , ID3D10ShaderResourceView * pView )
		: CTexture2D10( tex )
		, m_pView10( pView )
	{
	}
	// ~CShaderTexture2D10
	CShaderTexture2D10::~CShaderTexture2D10( void )
	{
		if( m_pView10 )
		{
			UINT rest = m_pView10->Release();
			if( rest != 0 )
			{
				TRACE_ERROR( _S("CShaderTexture2D10::DTOR: Invalid IDirect3DTexture9 reference count.") );
				FATAL_ABORT( _S("Invalid ID3D10ShaderResourceView reference count.") );
			}
		}
	}
	// GetTextureInterface
	IUnknown * CShaderTexture2D10::GetTextureInterface( void )
	{
		return m_pView10;
	}
	// GetTexFromView
	ID3D10Texture2D * CShaderTexture2D10::GetTexFromView( ID3D10ShaderResourceView * view )
	{
		ID3D10Texture2D * tex = NULL;
		view->QueryInterface( IID_ID3D10Texture2D , (void**)&tex );
		return tex;
	}

} // namespace xeres
